Rails on DockerをElastic Beanstalkにデプロイする
AWSアップデート祭りを横目に、認定デベロッパー目指して一人Elastic Beanstalkをあれこれ触っている、八幡です。
個人的な趣味もあり、AWSのサービスについて学ぶときはRubyやRailsを媒介者とすることが多いのですが、今回Elastic Beanstalkに"Rails on Docker"をデプロイしてみましたのでその手順をご紹介します。
構成
Elastic BeanstalkはRDS有りのシングルインスタンス環境です。
RailsアプリはRuby on Rails チュートリアルのサンプルアプリを使いました。(動かしたのはDevelopment環境です。)
Elastic BeanstalkにRails on Dockerをデプロイするやり方は幾通りか考えられるかと思いますが、今回はRailsアプリのソース、Dockerfile、Dockerrun.aws.jsonをローカルマシンに準備して、それらを基にEC2インスタンス上でDockerイメージをビルドする方式でやってみました。
前提
- ローカルマシン : Mac OSX 10.10.3
- 作成済みのAWSリソース
- Key Pair
- VPC(EC2用のPublic Subnet×1, RDS用のPrivate Subnet×2)
EB CLIのインストール
Elastic Beanstalkの環境作成やDockerコンテナのデプロイはEB CLIを使って行いますので、予めローカルマシンにインストールしておきます。
$ pip install awsebcli $ eb --version EB CLI 3.2.2 (Python 2.7.9)
Railsアプリの準備
DockerにデプロイするRailsアプリを準備します。
Github上のリポジトリをクローンした後、database.ymlを作成します。
$ git clone https://github.com/railstutorial/sample_app_rails_4.git $ cd sample_app_rails_4 $ cp config/database.yml.example config/database.yml
database.ymlにRDSへ接続するための設定を追加します。DBの情報は環境変数から読み込むようにします。
development: adapter: mysql2 database: ENV.fetch('RDS_DB_NAME') username: ENV.fetch('RDS_USERNAME') password: ENV.fetch('RDS_PASSWORD') host: ENV.fetch('RDS_HOSTNAME') port: ENV.fetch('RDS_PORT')
RDS有りのElastic Beanstalk環境を作成するとこれらの「RDS_*」はじまりの環境変数が自動的に設定され、Elastic Beanstalkへのデプロイ処理時にDockerコンテナに渡されます。
なおサンプルアプリではdatabase.ymlが.gitignoreに含まれているので、この設定を削除しておきます。
# Ignore other unneeded files. database.yml ←この行を削除(またはコメントアウト)する
Gemfileにはmysql2を追加します。
gem 'mysql2'
これでRailsアプリの準備は完了です。
Dockerfile, Dockerrun.aws.jsonの作成
Railsアプリのプロジェクトルート(今回の場合はsample_app_rails_4ディレクトリ直下)にDockerfileを作成します。公式レポジトリにあるRailsイメージを参考に、以下の内容で作成しました。
# RubyのバージョンはサンプルアプリのGemfileでの指定と合わせる FROM ruby:2.0 RUN mkdir -p /usr/src/app WORKDIR /usr/src/app ADD Gemfile /usr/src/app/ ADD Gemfile.lock /usr/src/app/ ADD start.sh /usr/src/app/bin/ RUN chmod +x /usr/src/app/bin/start.sh RUN bundle install ADD . /usr/src/app RUN apt-get update && apt-get install -y nodejs --no-install-recommends && rm -rf /var/lib/apt/lists/* RUN apt-get update && apt-get install -y mysql-client --no-install-recommends && rm -rf /var/lib/apt/lists/* EXPOSE 3000 CMD ["bin/start.sh"]
Dockerイメージのビルドの段階では上述の「RDS_*」はじまりの環境変数がコンテナから参照できないため、DBマイグレーション実行&Railsを起動するスクリプト(start.sh)を作成し、docker run時に同スクリプトを実行する形としました。
start.shの中身は以下のとおりです。
#!/bin/bash bundle exec rake db:migrate bundle exec rails server
Dockerrun.aws.jsonではDockerコンテナで公開するポートを設定します。
{ "AWSEBDockerrunVersion": "1", "Ports" : [{ "ContainerPort": "3000" }] }
ここまでの変更をコミットしておきます。
$ git add . $ git commit -m "prepare for eb deployment"
Elastic Beanstalkのアプリケーション作成
ここからはEB CLIを使って作業を行います。まずはじめにeb initコマンドでElastic Beanstalkアプリケーションを作成します。
$ eb init eb-docker-rails --platform docker --keyname aws-eb Application eb-docker-rails has been created.
eb-docker-railsがアプリケーション名です。オプションとしてプラットフォーム(docker)とキーペア名(aws-eb)を指定します。
クレデンシャル情報はAWS CLIと共通のものが使われます。クレデンシャル情報未設定の場合はeb initの初回実行時に入力を求められます。(アクセスキーとシークレットキー。)
Elastic Beanstalkの環境(ELB, EC2, RDS)作成
続いてeb createコマンドでElastic Beanstalkの環境(EC2, RDS)を作成します。
環境作成には時間がかかるので、--timeoutでコマンド実行のタイムアウトを長めに設定しておきます。(30分など)
eb createはElastic Beanstalkの環境の作成〜アプリケーション(今回の場合はDockerコンテナ)のデプロイまでを実行します。今回は--sampleで一旦AWSで用意されているサンプルアプリケーションをデプロイする形にしました。(環境の作成後に、改めてRailsアプリを含んだDockerコンテナをデプロイします。)
--singleを指定するとELBなしのシングルインスタンス構成の環境が作成されます。
その他のオプションについてはeb create --helpや 公式リファレンスをご参照ください。
$ eb create eb-docker-rails-dev \ --cname eb-rails-dev \ --database \ --sample \ --single \ --vpc \ --vpc.id vpc-34c81951 \ --vpc.publicip \ --vpc.ec2subnets subnet-e1bd1996 \ --vpc.securitygroups sg-2a72f14f \ --vpc.dbsubnets subnet-d2bd19a5,subnet-13629c4a \ --database \ --database.username ebroot \ --database.password <password> \ --instance_type t2.micro \ --tier webserver \ --timeout 30 # 今回はELBは使用しないので、以下はそのままEnter Enter a comma-separated list of Amazon ELB subnets: [Enter] Do you want the load balencer to be public? (Select no for internal) (y/n): [Enter]
「Successfully launched environment: <環境名>」と表示されれば作成完了です。
デプロイ
作成したElastic Beanstalkの環境へDockerコンテナをデプロイします。デプロイはeb deployで一撃です。Dockerコンテナのビルドに時間がかかるので、eb createと同様にタイムアウトを長めに設定しておきます。
$ eb deploy --timeout 30
eb deployを実行するとRailsアプリのソース、Dockerfile, Dockerrun.aws.json一式がzip形式でs3にアップロードされ、Dockerコンテナのデプロイが実行されます。「INFO: Environment update completed successfully.」と表示されればデプロイ完了です。
動作確認
eb openを実行するとWebブラウザが自動的に起動しElastic Beanstalkのエンドポイント(URL)にアクセスします。
以下の画面が表示されればデプロイ成功です。
まとめ
Elastic BeanstalkはRuby環境にも対応しているためRailsアプリを直接デプロイすることも可能ですが、 OSやWebアプリケーションサーバは選択肢が限定されます(Amazon Linux + Passenger or Puma)。 Dockerであればこれらの縛りを受けることがないため、Rubyの実行環境の選択肢が広がります 。なお、実運用観点ではRDSの文字コードやタイムゾーンなどあれこれ追加で設定が必要かと思いますが、今回はDocker on Elastic Beanstalkの検証が主目的のためその辺は省略しています。
EC2 Container ServiceもGA&東京に来たということで、今度はそちらでもRailsを動かしてみたいと思います。
参考URL
- Elastic Beanstalk開発者ガイド
- Docker Language Stackを使って一撃で開発環境を構築する | Developers.IO
- AWS Elastic BeanstalkでDockerコンテナをデプロイしてみた | Developers.IO
- AWS Black Belt Techシリーズ AWS Elastic Beanstalk
- VPC 内に Elastic Beanstalk + RDS の環境構築して Rails アプリをデプロイする - xykのブログ
- rbdockというRuby/Rails/Sinatra用のDockerfileを生成するgemをつくった | SOTA
- railsをdockerで動かしたい場合の構成はどうするべきか - Qiita
- Deploy Rails Application on Docker with Elasticbeanstalk